﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
namespace Json2Hsm
{
    public class CreateFile
    {
        AllJsonMachines json;

        Dictionary<string,Param> paramList = new Dictionary<string, Param>();
        Machine rootMachine = new Machine();
        Dictionary<string, Machine> machineList = new Dictionary<string, Machine>();
        string[] logicStr = { "==", "!=", ">", "<", ">=", "<=" };
        public CreateFile(AllJsonMachines json)
        {
            this.json = json;

            rootMachine = new Machine() { depth = 0, name = "BaseLayer" };
            machineList.Add(rootMachine.name, rootMachine);
            foreach (JsonParam p in json.parameters)
            {
                paramList.Add(p.param, new Param() { init = p.init, type = p.type, name = p.param });
            }
            for (int i = 0; i < json.mainStateMachine.subStateMachines.Length; i++)
            {
                Machine machine = new Machine() { name = json.mainStateMachine.subStateMachines[i], depth = 1 ,parent = rootMachine.name};
                if (!machineList.ContainsKey(json.mainStateMachine.subStateMachines[i]))
                { machineList.Add(json.mainStateMachine.subStateMachines[i], machine); }
                rootMachine.subStates.Add(json.mainStateMachine.subStateMachines[i]);
            }
            for (int i = 0; i < json.mainStateMachine.subStates.Length; i++)
            {
                Machine machine = new Machine() {name = json.mainStateMachine.subStates[i], depth = 1 , parent = rootMachine.name };
                if (!machineList.ContainsKey(json.mainStateMachine.subStates[i]))
                { machineList.Add(json.mainStateMachine.subStates[i], machine); }
                rootMachine.subStates.Add(json.mainStateMachine.subStates[i]);
            }

            for (int i = 0; i < json.subStateMachines.Length; i++)
            {
                Machine machine;
                if (!machineList.ContainsKey(json.subStateMachines[i].name))
                {
                    machine = new Machine();
                    machineList.Add(json.subStateMachines[i].name, machine); 
                }
                else
                {
                    machine = machineList[json.subStateMachines[i].name];
                }
                machine.parent = json.subStateMachines[i].upStateMachine;
                machine.name = json.subStateMachines[i].name;
                machine.depth = machineList[machine.parent].depth + 1;
                
                for (int j = 0; j < json.subStateMachines[i].subStates.Length;j++)
                {
                    Machine sub = new Machine();
                    sub.depth = machine.depth + 1;
                    sub.name = json.subStateMachines[i].subStates[j];
                    sub.parent = machine.name;
                    machine.subStates.Add(sub.name);
                    if (!machineList.ContainsKey(sub.name))
                    { machineList.Add(sub.name, sub); }
                }
                for (int j = 0; j < json.subStateMachines[i].subStateMachines.Length; j++)
                {
                    Machine sub = new Machine();
                    sub.depth = machine.depth + 1;
                    sub.name = json.subStateMachines[i].subStateMachines[j];
                    sub.parent = machine.name;
                    machine.subStates.Add(sub.name);
                    if (!machineList.ContainsKey(sub.name))
                    { machineList.Add(sub.name, sub); }
                }
            }

            foreach (JsonState s in json.states)
            {
                Machine state = machineList[s.state];
                foreach (JsonTrans tr in s.transitions)
                {
                    Trans trans = new Trans();
                    trans.toState = machineList[tr.toState];
                    foreach (JsonConditions c in tr.conditions)
                    {
                        Condition condition = new Condition() { logic = c.logic, value = c.value, param = paramList[c.param] };
                        trans.conditions.Add(condition);
                    }
                    state.trans.Add(trans);
                }
            }
        }

        /// <summary>
        /// 获取状态名
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
         string GetStateName(Machine s)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(machineList[s.parent].name);
            sb.Append("_");
            sb.Append(s.name);
            return sb.ToString();
        }

        /// <summary>
        /// 获取状态机变量命
        /// </summary>
        /// <param name="m"></param>
        /// <returns></returns>
        string GetStateMachineVar(Machine m)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(m.name);
            sb.Append("State");
            return sb.ToString();
        }
        UInt64 enumIndex = 0;
        /// <summary>
        /// 获取状态机枚举
        /// </summary>
        /// <param name="m"></param>
        /// <returns></returns>
        string GetStateMachineEnum(Machine m)
        {
            StringBuilder sb = new StringBuilder();
            if(m.subStates.Count == 0)
            {
                return "";
            }
            sb.AppendLine("enum{");
            foreach (string s in m.subStates)
            {
                sb.AppendFormat("    {0} = {1},\r\n", machineList[s].codeName, enumIndex.ToString());
                enumIndex++;
            }
            sb.AppendLine("};");
            return sb.ToString();
        }
        public string outputH()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("#ifndef __SM_H");
            sb.AppendLine("#define __SM_H");

            sb.AppendLine("#ifdef SM_GLOBAL\r\n    #define EXT\r\n#else\r\n    #define EXT extern\r\n#endif\r\n");

            foreach(Machine m in machineList.Values)
            {
                sb.Append(GetStateMachineEnum(m));
            }

            sb.AppendLine("// states variable");
            sb.AppendLine("#define STATE_DEFINE(x) EXT unsigned char x;EXT unsigned char x##To;");
            foreach (Machine m in machineList.Values)
            {
                if(m.subStates.Count > 0)
                {
                    sb.AppendFormat("STATE_DEFINE({0})\r\n", m.codeVarName);
                }
            }

            ///将event类型和int类型分开
            List<Param> eventParam = new List<Param>();
            List<Param> valueParam = new List<Param>();
            foreach (Param p in paramList.Values)
            {
                if (p.type != 2)
                {
                    eventParam.Add(p);
                }
                else
                {
                    valueParam.Add(p);
                }
            }

            sb.AppendLine("// param");
            sb.AppendLine("#define PARAM_TRIGGER\\");
            foreach (Param p in eventParam)
            {
                sb.AppendFormat("    PARAM_TRIGGER_DEFINE({0})\\\r\n",p.name);
            }
            sb.AppendLine();

            sb.AppendLine("#define PARAM_VALUE\\");
            foreach (Param p in valueParam)
            {
                sb.AppendFormat("    PARAM_VALUE_DEFINE({0})\\\r\n", p.name);
            }
            sb.AppendLine();


            sb.AppendLine("#define PARAM_TRIGGER_DEFINE(x) unsigned char x:1;");
            sb.AppendLine("#define PARAM_VALUE_DEFINE(x) unsigned long x;");
            sb.AppendLine("typedef struct param{");
            sb.AppendLine("    PARAM_TRIGGER");
            sb.AppendLine("    PARAM_VALUE");
            sb.AppendLine("} param;\r\nEXT param my_param;");
            sb.AppendLine("#undef PARAM_VALUE_DEFINE");
            sb.AppendLine("#undef PARAM_TRIGGER_DEFINE");
            sb.AppendLine();

            sb.AppendLine("void sm_state_change();");
            sb.AppendLine("void sm_state_loop();");
            sb.AppendLine("#define sm_param_trigger(x) do{x = 1;sm_state_change();}while(0)");
            sb.AppendLine("#define sm_value_up(x) do{x++;sm_state_change();}while(0)");
            sb.AppendLine("#define sm_value_down(x) do{x--;sm_state_change();}while(0)");
            sb.AppendLine("#define sm_value_set(x,y) do{x=y;sm_state_change();}while(0)");
            sb.AppendLine();

            sb.AppendLine("// user function");
            sb.AppendLine("void root_change_state(unsigned char from, unsigned char to);");

            foreach (Machine m in machineList.Values)
            {
                if (m.subStates.Count > 0)
                {
                    sb.AppendFormat("void {0}(unsigned char from, unsigned char to);\r\n",m.funcName);
                    sb.AppendFormat("void {0}_change(unsigned char state);\r\n",m.codeVarName);
                }
            }
            sb.AppendLine();

            foreach(Machine s in machineList.Values)
            {
                if(s == rootMachine)
                {
                    continue;
                }
                sb.AppendFormat("void {0}_loop();\r\n",s.codeName);
            }
            sb.AppendLine("#endif");
            return sb.ToString();
        }
        private string OutputConditions(List<Condition> conditions)
        {
            StringBuilder sb = new StringBuilder();
            foreach (Condition c in conditions)
            {
                StringBuilder sub = new StringBuilder();
                sub.Append("(my_param.");
                sub.Append(c.param.name);
                if (c.param.type == 2)
                {
                    sub.Append(logicStr[c.logic]);
                    sub.Append(c.value.ToString());
                }
                sub.Append(")&&");
                sb.Append(sub);
            }
            sb.Remove(sb.Length - 2, 2);
            return sb.ToString();

        }
        private string outputTrans(Machine m,Trans t)
        {
            StringBuilder sb = new StringBuilder();
            Machine parent = machineList[m.parent];
            sb.AppendFormat("if({0})\r\n{{\r\n{1}To = {2};\r\n}}\r\n", OutputConditions(t.conditions),parent.codeVarName,t.toState.codeName);

            return sb.ToString();
        }

        private string outputTransUP(Machine m, Trans t)
        {
            StringBuilder sb = new StringBuilder();
            Machine parent = machineList[t.toState.parent];
            sb.AppendFormat("if({0})\r\n{{\r\n{1}To = {2};\r\n}}\r\n", OutputConditions(t.conditions), parent.codeVarName, t.toState.codeName);
            return sb.ToString();
        }

        private string outputDown(Machine m,Machine t)
        {
            StringBuilder sb = new StringBuilder();
            Machine parent = machineList[t.parent];

            sb.AppendFormat("{0}To = {1};\r\n",parent.codeVarName,t.codeName);

            if(m.parent != parent.name)
            {
                sb.Append(outputDown(m, parent));
            }
            
            return sb.ToString();
        }
        private string outputTransDown(Machine m, Trans t)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("if({0})\r\n{{\r\n{1}}}\r\n", OutputConditions(t.conditions), outputDown(m, t.toState));
            return sb.ToString();
        }

        public string outputMachineSM(Machine m)
        {
            if(m.subStates.Count == 0)
            {
                return "";
            }
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("switch({0})\r\n{{\r\n", m.codeVarName);

            foreach (string str in m.subStates)
            {
                Machine s = machineList[str];
                sb.AppendFormat("case {0}:\r\n",s.codeName);

                foreach (Trans t in s.trans)
                {
                    if (t.toState.depth == s.depth)
                    {
                        sb.Append(outputTrans(s, t));
                    }
                    else if (t.toState.depth < s.depth)
                    {
                        sb.Append(outputTransUP(s, t));
                    }
                    else
                    {
                        sb.Append(outputTransDown(s, t));
                    }
                }
                if(s.subStates.Count > 0)
                {
                    sb.Append(outputMachineSM(s));
                }
                sb.AppendLine("break;");
            }
            sb.AppendLine("}");
            return sb.ToString();
        }

        private string outputMachineLoop(Machine m)
        {
            if(m.subStates.Count == 0)
            {
                return "";
            }
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("switch({0})\r\n{{\r\n", m.codeVarName);

            foreach (string str in m.subStates)
            {
                Machine s = machineList[str];
                sb.AppendFormat("case {0}:\r\n    {0}_loop();\r\n", s.codeName);
                if(s.subStates.Count > 0)
                {
                    sb.Append(outputMachineLoop(s));
                }
                sb.AppendLine("break;");
            }
            sb.AppendLine("}");
            return sb.ToString();
        }

        public string outputSMFile()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("#define SM_GLOBAL\r\n#include \"sm.h\"");
            foreach (Machine m in machineList.Values)
            {
                if (m.subStates.Count > 0)
                {
                    sb.AppendFormat("void {0}_change(unsigned char state)\r\n{{\r\n{0} = state;\r\n}}\r\n", m.codeVarName);
                }
            }
            sb.AppendLine("void sm_state_change()\r\n{");
            sb.AppendLine(outputMachineSM(rootMachine));

            foreach (Machine m in machineList.Values)
            {
                if (m.subStates.Count > 0)
                {
                    sb.AppendFormat("if({0} != {0}To)\r\n{{\r\n{1}({0},{0}To);\r\n{0} = {0}To;\r\n}}\r\n", m.codeVarName,m.funcName);
                }
            }

            sb.AppendLine("#define PARAM_TRIGGER_DEFINE(x) my_param.##x = 0;");
            sb.AppendLine("PARAM_TRIGGER");
            sb.AppendLine("#undef PARAM_TRIGGER_DEFINE");

            //foreach (Param p in paramList.Values)
            //{
            //    if (p.type != 2)
            //    {
            //        sb.Append("my_param.");
            //        sb.Append(p.name);
            //        sb.AppendLine(" = 0;");
            //    }
            //}

            sb.AppendLine("}");

            sb.AppendFormat("void sm_state_loop()\r\n{{\r\n{0}}}", outputMachineLoop(rootMachine));
            return sb.ToString();
        }

        public string outputSMUserFile(string str)
        {
            if (!str.Contains("#include \"sm.h\""))
            {
                str = str.Insert(0, "#include \"sm.h\"\r\n");
            }
            
            foreach (Machine m in machineList.Values)
            {
                if (m.subStates.Count > 0)
                {
                    if (!str.Contains(m.funcName + "(unsigned char from, unsigned char to)"))
                    {
                        str += "void " + m.funcName + "(unsigned char from, unsigned char to)\r\n{\r\n}\r\n";
                        //str += "{\r\nswitch(from){";
                        //foreach (string st in m.subStates)
                        //{
                            
                        //    Machine state = machineList[st];
                        //    if(state.trans.Count > 0)
                        //    {
                        //        List<string> list = new List<string>();
                        //        str += "\r\ncase " + state.codeName + ":";
                        //        str += "\r\nswitch(to){";

                        //        foreach (Trans tra in state.trans)
                        //        {
                        //            if (!list.Contains(tra.toState.codeName))
                        //            {
                        //                str += "\r\ncase " + tra.toState.codeName + ":\r\n";
                        //                str += state.codeName + "_To_" + tra.toState.codeName + "();";
                        //                str += "\r\nbreak;";
                        //                list.Add(tra.toState.codeName);
                        //            }

                        //        }
                        //        str += "\r\ndefault:\r\nbreak;\r\n}";
                        //        str += "\r\nbreak;";
                        //    }
                            
                        //}
                        //str += "\r\ndefault:\r\nbreak;}\r\n}\r\n";
                    }
                }
            }

            //foreach (Machine s in machineList.Values)
            //{
            //    foreach (Trans trans in s.trans)
            //    {
            //        string funcName = "void " + s.codeName + "_To_" + trans.toState.codeName + "()";
            //        if (!str.Contains(funcName))
            //        {
            //            str += funcName + "\r\n";
            //            str += "{\r\n    \r\n}\r\n";
            //        }
            //    }

            //}


            foreach (Machine s in machineList.Values)
            {
                string funcName = "void " + s.codeName + "_loop()";
                if (!str.Contains(funcName) && s != rootMachine)
                {
                    str += funcName + "\r\n";
                    str += "{\r\n    \r\n}\r\n";
                }
            }

            return str;
        }
    }
}
